Skip to content

Add scala.compiletime.requireConst #9764

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 15, 2020

Conversation

nicolasstucki
Copy link
Contributor

@nicolasstucki nicolasstucki commented Sep 10, 2020

requireConst checks at compiletime that the provided values is a constant after inlining and constant folding.

Usage:

inline def twice(inline n: Int): Int =
  requireConst(n) // static assertion that the parameter `n` is a constant
  n + n

twice(1)
val m: Int = ...
twice(m) // error: expected a constant value but found: m

@nicolasstucki
Copy link
Contributor Author

This is an ability we lost when we fixed the semantics of inline parameters. Currently, we can only do these checks using macros.

@nicolasstucki

This comment has been minimized.

@nicolasstucki nicolasstucki marked this pull request as ready for review September 10, 2020 11:28
@smarter
Copy link
Member

smarter commented Sep 10, 2020

const(n) // static assertion that the parameter n is a constant

I find it weird that the same syntax is used in if as a condition and as an assertion. I think the assertion should be more explicit, like requireConst(n)

@nicolasstucki
Copy link
Contributor Author

nicolasstucki commented Sep 10, 2020

There is only one method, that happens to be used in both use cases. If I rename it we would end up with

  if requireConst(n == 0) then 1L
  else if requireConst(n % 2 == 1) then x * power(x, n - 1)

which is a bit too long.

@sjrd
Copy link
Member

sjrd commented Sep 10, 2020

I think that requireConst is better than const. I had shared the same misunderstanding as @smarter, so clearly this is not an isolated problem. If we, who are very used to imagine what it could mean for a compiler, were confused by this in the same way, it's quite likely that non-compiler-developers will be even more confused.

The fact that it's bit longer should not stand in the way of clarity.

@nicolasstucki
Copy link
Contributor Author

Ok. I changed const to requireConst.

@nicolasstucki nicolasstucki changed the title Add scala.compiletime.const Add scala.compiletime.requireConst Sep 10, 2020
@smarter
Copy link
Member

smarter commented Sep 10, 2020

I still find it weird that the same method call does something different (return a value or emitting an error) depending on where it's used syntactically. I don't think we've ever had something like that in Scala.

@sjrd
Copy link
Member

sjrd commented Sep 10, 2020

That's not what's happening here. In both cases it a) emits an error if the argument is not constant, and b) returns the constant value of the argument if it is constant.

In the first example, the returned value is just discarded.

@smarter
Copy link
Member

smarter commented Sep 10, 2020

Ah I see, I misread if const(foo) and if requireConst(foo) as "if foo is const" which is maybe a sign this isn't the best syntax.

@nicolasstucki
Copy link
Contributor Author

The doc should explicitly state that the value is returned.

@nicolasstucki
Copy link
Contributor Author

Alternative names

  • requiredConst
  • assertedConst

These names seem to indicate that the value is returned

@nicolasstucki nicolasstucki force-pushed the add-compiletime-const branch 2 times, most recently from 90fbfdb to 01bc23d Compare September 14, 2020 19:20
`requireConst` checks at compiletime that the provided values is a constant after inlining and constant folding.

Usage:
```scala
inline def twice(inline n: Int): Int =
  requireConst(n) // static assertion that the parameter `n` is a constant
  n + n

twice(1)
val m: Int = ...
twice(m) // error: expected a constant value but found: m
```
@nicolasstucki
Copy link
Contributor Author

@liufengyun I updated this PR to match what we agreed during the meeting. Now requireConst returns Unit and I removed the special handling in the inline if. I also made the argument type more precise. Unlike other intrinsic macros, there is only a check done but the code generation is just the normal inlining of the method.

inline def requireConst(inline x: Boolean | Byte | Short | Int | Long | Float | Double | Char | String): Unit = ()

Copy link
Contributor

@liufengyun liufengyun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Side note: I find this is a very good practice to express pre-conditions in meta-programming.
I can foresee similar APIs will be helpful in macros as well. It helps simplify error handling code and makes the assumptions clear.

@nicolasstucki nicolasstucki merged commit 89af58f into scala:master Sep 15, 2020
@nicolasstucki nicolasstucki deleted the add-compiletime-const branch September 15, 2020 10:52
@Kordyjan Kordyjan added this to the 3.0.0 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants